﻿Shader "Tiny Shader/Toon"
{
    Properties
    {
        // 模型颜色叠加
        _Color ("Color", Color) = (0.5, 0.65, 1, 1)
        // 主纹理
        _MainTex ("MainTex", 2D) = "white" {}
        // 环境光的颜色（支持高动态范围）
        [HDR]
        _AmbientColor ("Ambient Color", Color) = (0.4, 0.4, 0.4, 1)
        // 高光颜色（支持高动态范围）
        [HDR]
        _SpecularColor ("Specular Color", Color) = (0.9, 0.9, 0.9, 1)
        // 高光系数，控制高光区域大小和亮度
        _Glossiness ("Glossiness", Float) = 32
        // 边缘光颜色（支持高动态范围）
        [HDR]
        _RimColor ("Rim Color", Color) = (1, 1, 1, 1)
        // 边缘光系数，用于微调边缘光的范围
        _RimAmount ("Rim Amount", Range(0, 1)) = 0.716
        // 边缘光系数，用于控制边缘光受光照方向的影响
        _RimThreshold ("Rim Threshold", Range(0, 1)) = 0.1
    }

    SubShader
    {
        Tags
        {
            "RenderType" = "Opaque"
        }

        Pass
        {
            Tags {
                // 基本的前向渲染光照模式，请求将一些光照数据传入当前Shader
                "LightMode" = "ForwardBase"
                // 只传递平行光、环境光/lightprob数据
                "PassFlags" = "OnlyDirectional"
            }

            CGPROGRAM
            // 与FawardBase配合使用
            #pragma multi_compile_fwdbase
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            // 一些内置的光照变量
            #include "AutoLight.cginc"
            #include "Lighting.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 worldNormal : NORMAL;
                float3 viewDir : TEXCOORD1;
                SHADOW_COORDS(2)
            };

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _AmbientColor;
            fixed4 _SpecularColor;
            float _Glossiness;
            fixed4 _RimColor;
            float _RimAmount;
            float _RimThreshold;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                // 将世界空间的法线方向传入片元着色器
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                // 将世界空间的视线方向传入片元着色器
                o.viewDir = WorldSpaceViewDir(v.vertex);

                TRANSFER_SHADOW(o);

                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float shadow = SHADOW_ATTENUATION(i);

                // 法线方向
                float3 normal = normalize(i.worldNormal);
                // 法线方向和光照方向的点乘，后面用于控制光照强度
                float NdotL = dot(_WorldSpaceLightPos0, normal);
                // 光照强度
                float lightIntensity = smoothstep(0, 0.02, NdotL * shadow);
                // 漫反射
                fixed4 diffuse = _LightColor0 * lightIntensity;

                // 视线方向
                float3 viewDir = normalize(i.viewDir);
                // 光照方向和实现方向的中间向量
                float3 halfDir = normalize(_WorldSpaceLightPos0 + viewDir);
                // 法线方向和中间向量的点乘（Blinn-Phong光照模型）
                float NdotH = dot(normal, halfDir);

                // 高光强度，_Glossiness做平方处理，是为了让这个值控制的高光更细腻（基于经验）
                float specularIntensity = pow(NdotH * lightIntensity, _Glossiness * _Glossiness);
                // 卡通化处理，防止高光明暗交接处线性过渡
                float specularIntensitySmooth = smoothstep(0.005, 0.05, specularIntensity);
                // 高光处的光照
                fixed4 specular = specularIntensitySmooth * _SpecularColor;

                // 边缘光的点乘，用1减去点乘结果，为了得到正相关，控制边缘光的强度(越边缘，边缘光强度越大）
                float4 rimDot = 1 - dot(viewDir, normal);
                // 边缘光的强度
                // 为了让边缘光受光照在表面方向的影响，将rimDot乘以NdotL
                // 为了更细腻地控制光照方向对边缘光的影响，将NdotL进行pow处理，用_RimThreshold参数进行控制
                float rimIntensity = rimDot * pow(NdotL, _RimThreshold);
                // 卡通化处理，防止边缘光明暗交接处线性过渡，用_RimAmount来控制边缘光显示的范围
                rimIntensity = smoothstep(_RimAmount - 0.01, _RimAmount + 0.01, rimIntensity);
                // 边缘光
                fixed4 rim = rimIntensity * _RimColor;

                // 纹理采样颜色
                fixed4 sampleColor = tex2D(_MainTex, i.uv);

                // 纹理颜色乘以滤色，再乘以Blinn-Phong光照模型卡通化后的颜色，得到最终的效果
                // 基于Blinn-Phong光照模型的卡通化光照模型：环境光 + 漫反射 + 高光 + 边缘光
                return sampleColor * _Color * (_AmbientColor + diffuse + specular + rim);
            }
            ENDCG
        }

        UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
    }
}
